iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Claude AI

從零開始 Claude Code:30 天打造 AI 任務管理 Web App系列 第 11 篇

[Day 11] 讓任務有地方保存:開始接 MySQL

  • 分享至 

  • xImage
  •  

Day 10 已經可以從表單把任務送到 Flask,終端機也會看到:

收到新任務: {
    'title': '測試',
    'description': '測試',
    'status': '待處理',
    'category': '學習',
    'due_date': '2026-09-24'
}

但這筆資料目前只是被 Flask 收到而已。
只要重新整理頁面、關掉程式,資料就沒有真正保存。
所以 Day 11 要做的事情很明確:
先把 Flask 和 MySQL 接起來。
今天先不把新增任務寫進資料庫,只做到:

建立資料庫
    ↓
建立資料表
    ↓
建立 Flask 資料庫設定
    ↓
Flask 成功連上 MySQL

真正把 Day 10 的表單資料存進去,留到 Day 12。


先確認今天的範圍

我先把目前狀態交給 Claude Code:

目前專案狀態:

- Flask 首頁可以正常執行
- 已有新增任務表單
- POST /tasks/new 可以收到表單資料
- 目前資料只會顯示在頁面並 print 到終端機
- 還沒有資料庫連線

今天要開始接 MySQL。

今天只做到:

1. 更新 requirements.txt
2. 建立 .env
3. 建立 .env.example
4. 建立 .gitignore
5. 建立 config.py
6. 建立 db.py
7. 建立 schema.sql
8. 修改 app.py 接上 Flask-SQLAlchemy
9. 確認 Flask 可以成功連上 MySQL

今天不要:

- INSERT 任務
- 修改首頁任務列表
- 做分類 CRUD
- 做任務修改或刪除
- 建立 models/
- 建立 services/
- 建立 routes/
- 加入 Claude API

這次沒有再往後擴功能。


這次新增了哪些檔案?

完成後,專案變成:

ai-task-manager/
├── app.py
├── config.py
├── db.py
├── schema.sql
├── .env
├── .env.example
├── .gitignore
├── requirements.txt
│
├── templates/
│   ├── index.html
│   └── add_task.html
│
├── static/
│   └── css/
│       └── style.css
│
└── venv/

這次新增的重點主要是:

config.py
db.py
schema.sql
.env
.env.example
.gitignore

requirements.txt 加入資料庫相關套件

原本只有 Flask。
現在變成:

Flask
Flask-SQLAlchemy
PyMySQL
python-dotenv

這三個新增套件分別有不同用途。
Flask-SQLAlchemy 讓 Flask 可以使用 SQLAlchemy。
PyMySQL 負責 Python 和 MySQL 之間的連線。
python-dotenv 則讓程式可以讀取 .env 裡的設定。
安裝方式還是一樣:

pip install -r requirements.txt

為什麼要新增 .env?

資料庫連線需要:

Host
Port
Database
User
Password

這些如果直接寫在 app.py 裡,不太適合。
所以這次新增:

.env

內容大概是:

DB_HOST=localhost
DB_PORT=3306
DB_NAME=ai_task_manager
DB_USER=ai_task_manager_user
DB_PASSWORD=你的密碼

真正的 MySQL 密碼只放在自己的 .env。
不放進文章,也不放到 Git。


.env.example 又是什麼?

另外還建立:

.env.example

它跟 .env 結構一樣,但不放真實密碼:

DB_HOST=localhost
DB_PORT=3306
DB_NAME=ai_task_manager
DB_USER=ai_task_manager_user
DB_PASSWORD=your_password

這個檔案可以留在專案裡,讓之後的人知道需要哪些環境變數。
簡單來說:

.env
→ 真正設定
→ 不公開

.env.example
→ 設定範例
→ 可以放進 Git

.gitignore 是做什麼的?

這次也先建立:

.gitignore

內容是:

venv/
.env
__pycache__/
*.pyc

它的意思是告訴 Git:

這些檔案或資料夾不要追蹤

例如 .env 裡面有資料庫密碼,所以不應該上傳。
venv/ 則是虛擬環境,也不需要跟著專案一起上傳。
目前雖然還沒有 git init,但先準備好也沒問題。


config.py:集中管理資料庫設定

這次新增:

config.py

內容是:

import os

from dotenv import load_dotenv

load_dotenv()


class Config:
    SQLALCHEMY_DATABASE_URI = (
        f"mysql+pymysql://{os.environ['DB_USER']}:{os.environ['DB_PASSWORD']}"
        f"@{os.environ['DB_HOST']}:{os.environ['DB_PORT']}/{os.environ['DB_NAME']}"
        "?charset=utf8mb4"
    )

    SQLALCHEMY_TRACK_MODIFICATIONS = False

先看:

load_dotenv()

它會讀取 .env。
接著:

os.environ['DB_USER']
os.environ['DB_PASSWORD']
os.environ['DB_HOST']
os.environ['DB_PORT']
os.environ['DB_NAME']

會拿出對應設定。
最後組成類似:

mysql+pymysql://ai_task_manager_user:密碼@localhost:3306/ai_task_manager?charset=utf8mb4

這就是 Flask-SQLAlchemy 用來連 MySQL 的連線網址。


為什麼不是直接用 root?

這次另外建立:

ai_task_manager_user

而不是讓 Flask 一直使用:

root

這個專用帳號只需要存取:

ai_task_manager

這個資料庫。
這樣之後 Flask 使用資料庫時,不需要拿最高權限的 root 帳號來連。


db.py 很短,但之後會一直用到

這次的 db.py:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

目前只有兩行。
它先建立一個共用的:

db

之後不管是:

Task
Category

都可以使用同一個 db。
Day 11 還沒有建立 Model,所以目前只先把這個基礎準備好。


建立 ai_task_manager 資料庫

這次新增:

schema.sql

第一段是:

CREATE DATABASE IF NOT EXISTS ai_task_manager
    DEFAULT CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

USE ai_task_manager;

這裡建立:

ai_task_manager

作為這個專案使用的資料庫。
utf8mb4 則是為了避免中文或其他 Unicode 文字出現問題。


categories 資料表

第一張表:

CREATE TABLE categories (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE,
    color VARCHAR(7) DEFAULT '#6c757d'
) DEFAULT CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

目前有三個欄位:

id
name
color

id 是分類編號。
name 是分類名稱。
例如:

學習
專案
生活

color 則是之後可以拿來控制分類顯示顏色。


tasks 資料表

第二張表:

CREATE TABLE tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(200) NOT NULL,
    description TEXT NULL,
    status ENUM('todo', 'in_progress', 'done') NOT NULL DEFAULT 'todo',
    category_id INT NULL,
    due_date DATE NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id)
        REFERENCES categories(id)
        ON DELETE SET NULL
) DEFAULT CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

目前任務有:

id
title
description
status
category_id
due_date
created_at
updated_at

這些欄位已經開始跟 Day 10 的表單對起來。

任務名稱      → title
說明          → description
狀態          → status
分類          → category_id
截止日期      → due_date

另外:

created_at
updated_at

則是用來記錄建立與更新時間。


為什麼 status 存英文?

Day 10 畫面目前顯示:

待處理
進行中
已完成

但資料庫裡設定的是:

todo
in_progress
done

也就是:

todo         → 待處理
in_progress  → 進行中
done         → 已完成

之後程式可以用固定的值判斷狀態,畫面再顯示中文。
目前 Day 11 還沒有真的存資料,所以這部分先保留到下一步處理。


category_id 和分類的關係

tasks 沒有直接存:

學習

而是存:

category_id

例如:

categories

id | name
---|------
1  | 學習
2  | 專案
3  | 生活

如果某個任務屬於「學習」,那 tasks 裡可能是:

category_id = 1

這樣任務和分類就可以建立關聯。


ON DELETE SET NULL

這段:

ON DELETE SET NULL

代表如果某個分類之後被刪掉,任務不會一起消失。
只會把:

category_id

改成:

NULL

任務本身還是存在。


在 MySQL Workbench 建立資料表

我這次直接用 MySQL Workbench 執行 schema.sql。
執行完成後,再確認:

USE ai_task_manager;

SHOW TABLES;

可以看到:

categories
tasks

https://ithelp.ithome.com.tw/upload/images/20260925/20183857OaattRQGFm.png
也可以再執行:

DESCRIBE tasks;

確認 tasks 欄位都有建立成功。
https://ithelp.ithome.com.tw/upload/images/20260925/20183857Qgoca5y1le.png


建立專用 MySQL 帳號

這次沒有讓 Flask 直接使用 root。
而是建立:

ai_task_manager_user

在 Workbench 使用 root 連線執行:

CREATE USER IF NOT EXISTS
'ai_task_manager_user'@'localhost'
IDENTIFIED BY '自己設定的密碼';

GRANT ALL PRIVILEGES
ON ai_task_manager.*
TO 'ai_task_manager_user'@'localhost';

FLUSH PRIVILEGES;

這個帳號只用來操作:

ai_task_manager

建立完成後,我另外在 Workbench 新增一個:

AI Task Manager

連線。
設定:

Host: localhost
Port: 3306
User Name: ai_task_manager_user
Default Schema: ai_task_manager

再使用剛才設定的密碼測試連線。
連線成功會出現:
https://ithelp.ithome.com.tw/upload/images/20260925/20183857v8WeSAMOdg.png


app.py 怎麼接上資料庫?

Day 10 的 app.py 原本只有 Flask。
Day 11 多了:

from sqlalchemy import text

from config import Config
from db import db

接著:

app = Flask(__name__)

app.config.from_object(Config)
db.init_app(app)

這兩行是今天很重要的地方。

app.config.from_object(Config)

把 config.py 的設定載進 Flask。
接著:

db.init_app(app)

把剛才 db.py 建立的 SQLAlchemy 物件跟 Flask app 接起來。


用 SELECT 1 測試連線

今天還不會查任務,也不會新增任務。
所以 Claude Code 加了一個很單純的測試:

if __name__ == "__main__":
    try:
        with app.app_context():
            db.session.execute(text("SELECT 1"))
        print("MySQL 連線成功")
    except Exception as e:
        print("MySQL 連線失敗:", e)

    app.run(debug=True)

其中:

db.session.execute(text("SELECT 1"))

不是在查 tasks。
它只是送一個最簡單的 SQL 給 MySQL。
如果 MySQL 可以正常收到並回應,就代表資料庫連線成功。
實際啟動 Flask 後,終端機會看到:
https://ithelp.ithome.com.tw/upload/images/20260925/20183857gUzawN5DgM.png
這就是今天最重要的驗證結果。


現在新增任務還是不會進資料庫

雖然 MySQL 已經連線成功,但是 Day 10 的:

submitted_task = {
    "title": request.form.get("title", ""),
    "description": request.form.get("description", ""),
    "status": request.form.get("status", ""),
    "category": request.form.get("category", ""),
    "due_date": request.form.get("due_date", ""),
}

目前還是只有:

print("收到新任務:", submitted_task)

沒有真正執行資料庫新增。
所以現在流程是:

新增任務表單
    ↓
  Flask
    ↓
 print()

而另一邊:

  Flask
    ↓
  MySQL

已經確認可以連線。
但兩條線還沒有接在一起。


今天做到這裡

Day 11 完成之後,專案第一次有了真正的資料庫環境。
目前已經做到:

.env
  ↓
config.py
  ↓
Flask
  ↓
SQLAlchemy
  ↓
PyMySQL
  ↓
MySQL

MySQL 裡也已經有:

ai_task_manager
├── categories
└── tasks

而 Flask 啟動後也能成功印出:

MySQL 連線成功

下一步才會真正把 Day 10 的表單和 MySQL 接在一起:

新增任務表單
  ↓
Flask
  ↓
INSERT
  ↓
tasks

上一篇
[Day 10] 第一個功能:新增任務
下一篇
[Day 12] 把任務真的存進 MySQL
系列文
從零開始 Claude Code:30 天打造 AI 任務管理 Web App 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言